// Copyright 2008 web2me.dev
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.

package net.web2me.parser;

import net.web2me.api.CommonConstants;
import net.web2me.api.ParserException;
import net.web2me.util.DomUtil;
import net.web2me.util.Downloader;
import net.web2me.util.Validator;

import org.apache.log4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class StandardParser extends AbstractBaseParser {

	private static final String TYPES = "types";

	private static final String T = "t";

	private static final String G = "g";

	private static final String M = "m";

	private static final String K = "k";

	private static final String B = "b";

	private static Logger logger = Logger.getLogger(StandardParser.class);

	private final static String PROP_REGEX = "regex";

	private final static String PROP_TYPES = TYPES;

	private final static String PROP_LARGER_THAN = "larger.than";

	private final static String LARGER_THAN_FORMAT_REGEX = "(\\d)+[bkmgt]$";

	private String regex;

	private boolean errorShowed = false;

	private String[] DEFAULT_FORMATS = { "jpg", "jpeg", "png", "mpg", "wmv", "mpeg", "mov", "qt", "avi" };

	public void process(String siteStream) throws ParserException {

		if (System.getProperty(TYPES) == null) {
			logger.warn("No file types specified. Use the system property -Dtypes=zip:mp3:exe");
			logger.warn("Assuming images and videos");
		}

		regex = System.getProperty(PROP_REGEX);

		Document site = null;
		try {
			site = DomUtil.generateDocument(siteStream);
		} catch (Exception e) {
			throw new ParserException(e);
		}

		NodeList objectTags = site.getElementsByTagName(CommonConstants.OBJECT);

		NodeList links = site.getElementsByTagName(CommonConstants.ANCHOR);

		processNodeList(objectTags);

		processNodeList(links);
	}

	private void processNodeList(NodeList list) throws ParserException {
		for (int i = 0; i < list.getLength(); i++) {
			Element object = (Element) list.item(i);
			String url = object.getAttribute(CommonConstants.HREF);
			
			if (!((regex != null && url.matches(regex)) || regex == null)) {
				continue;
			}
			
			if (!Validator.isValidFileType(url, getFileTypes())) {
				continue;
			}

			try {
				url = toAbsoluteUrl(url);
				
				Downloader downloader = new Downloader();
				downloader.setUrl(url);

				if (System.getProperty(PROP_LARGER_THAN) != null) {
					if (downloader.getFileSize() < getLargenThanSize()) {
						logger.debug("Avoiding smaller file: " + url);

						continue;
					}
				}

				downloader.setDestinyDirectory(getDir());
				downloader.setReferer(getUrl());

				logger.debug("Downloading file: " + url);

				downloader.download();
			} catch (Throwable t) {
				throw new ParserException(t);
			}
		}
	}

	public String toAbsoluteUrl(String url) {
		logger.debug("Transforming \"" + url + "\" to an absolute URL");

		String finalUrl = CommonConstants.EMPTY;
		
		if (url.indexOf(CommonConstants.HTTP_DOT_DOT_SLASH_SLASH, 0) != -1) {
			return url;
		}

		try {
			if (CommonConstants.SLASH.equals(url.substring(0, 1))) {
				String host = getUrl().substring(7,
						getUrl().indexOf(CommonConstants.SLASH, 7));
				finalUrl = CommonConstants.HTTP_DOT_DOT_SLASH_SLASH + host + url;
			} else {
				logger.debug("Before anything: " + finalUrl);
				finalUrl = getUrl();

				// Remove the querystring
				if (finalUrl.indexOf(CommonConstants.QUESTION) > -1) {
					finalUrl = getUrl().substring(0, getUrl().indexOf(CommonConstants.QUESTION));
				}

				logger.debug("Before 2nd step: " + finalUrl);

				// Remove any web page after the last slash
				if (finalUrl.lastIndexOf(CommonConstants.SLASH) > -1) {
					finalUrl = finalUrl.substring(0, finalUrl
							.lastIndexOf(CommonConstants.SLASH) + 1);
				}

				logger.debug("Before last step: " + finalUrl);

				if (CommonConstants.EMPTY.equals(finalUrl)) {
					finalUrl = getUrl() + CommonConstants.SLASH + url;
				} else {
					finalUrl += url;
				}
			}
		} catch (Exception e) {
			logger.error("Exception caught", e);
		}

		logger.debug("Absolute URL: " + finalUrl);

		return finalUrl;
	}

	public String[] getFileTypes() {
		String tmpTypes = System.getProperty(PROP_TYPES);

		if (tmpTypes != null) {
			return tmpTypes.split(CommonConstants.COLON);
		}

		return DEFAULT_FORMATS;
	}

	private int getLargenThanSize() {
		String givenSize = System.getProperty(PROP_LARGER_THAN);
		int finalSize = 0;

		if (givenSize.matches(LARGER_THAN_FORMAT_REGEX)) {
			if (givenSize.endsWith(B)) {
				// Bytes
				return Integer.parseInt(givenSize.replace(B, CommonConstants.EMPTY));
			} else if (givenSize.endsWith(K)) {
				// Kilobytes
				return Integer.parseInt(givenSize.replace(K, CommonConstants.EMPTY)) * 1024;
			} else if (givenSize.endsWith(M)) {
				// Megabytes
				return Integer.parseInt(givenSize.replace(M, CommonConstants.EMPTY)) * 1024 * 1024;
			} else if (givenSize.endsWith(G)) {
				// Gigabytes
				return Integer.parseInt(givenSize.replace(G, CommonConstants.EMPTY)) * 1024 * 1024 * 1024;
			} else if (givenSize.endsWith(T)) {
				// Terabytes
				return Integer.parseInt(givenSize.replace(T, CommonConstants.EMPTY)) * 1024
						* 1024 * 1024 * 1024;
			} else {
				showLargenThanSizeError();
			}
		} else {
			showLargenThanSizeError();
		}

		return finalSize;
	}

	private void showLargenThanSizeError() {
		if (!errorShowed) {
			errorShowed = true;

			logger.warn("The specified \"" + PROP_LARGER_THAN + "\" property is not valid");
			logger.warn("Specify it as a number followed by: b, k, m, g or t " +
					"(Bytes, Kilobytes, Megabytes, Gigabytes and Terabytes");
			logger.warn("E.g: 100k");
		}
	}

	public String getDescription() {
		return "Parses the links to media files from any site that don't need a special treatment";
	}

	public String getName() {
		return "Standard";
	}
}
